
using System;
using System.IO;
using System.Collections;



namespace NSQLObjects.VS2010Plugin.Syntax {
public class Token {
	public int kind;    // token kind
	public int pos;     // token position in the source text (starting at 0)
	public int col;     // token column (starting at 1)
	public int line;    // token line (starting at 1)
	public string val;  // token value
	public Token next;  // ML 2005-03-11 Tokens are kept in linked list
}



//-----------------------------------------------------------------------------------
// Scanner
//-----------------------------------------------------------------------------------
public class Scanner {
	const char EOL = '\n';
	const int EOF = char.MaxValue + 1;

	const int eofSym = 0; /* pdt */
	const int maxT = 39;
	const int noSym = 39;


	public TextReader buffer; // scanner buffer
	
	Token t;          // current token
	int ch;           // current input character
	int pos;          // byte position of current character
	int col;          // column number of current character
	int line;         // line number of current character
	int oldEols;      // EOLs that appeared in a comment;
	static readonly Hashtable start; // maps first token character to start state

	Token tokens;     // list of tokens already peeked (first token is a dummy)
	Token pt;         // current peek token
	
	char[] tval = new char[128]; // text of current token
	int tlen;         // length of current token
	
	private class Buffer {
		internal const int EOF = char.MaxValue + 1;
	}

	static Scanner() {
		start = new Hashtable(128);
		for (int i = 65; i <= 90; ++i) start[i] = 1;
		for (int i = 97; i <= 122; ++i) start[i] = 1;
		for (int i = 48; i <= 57; ++i) start[i] = 2;
		start[91] = 14; 
		start[34] = 8; 
		start[47] = 15; 
		start[46] = 18; 
		start[44] = 19; 
		start[59] = 20; 
		start[42] = 21; 
		start[63] = 22; 
		start[58] = 23; 
		start[45] = 24; 
		start[123] = 25; 
		start[125] = 26; 
		start[40] = 27; 
		start[41] = 28; 
		start[Buffer.EOF] = -1;

	}
	
	public Scanner (TextReader buffer) {
		this.buffer = buffer;
		Init();
	}
	
	
	void Init() {
		pos = -1; line = 1; col = 0;
		oldEols = 0;
		NextCh();
		pt = tokens = new Token();  // first token is a dummy
	}
	
	void NextCh() {
		if (oldEols > 0) { ch = EOL; oldEols--; } 
		else {
			pos++;
			ch = buffer.Read(); col++;
			// replace isolated '\r' by '\n' in order to make
			// eol handling uniform across Windows, Unix and Mac
			if (ch == '\r' && buffer.Peek() != '\n') ch = EOL;
			if (ch == EOL) { line++; col = 0; }
			if (ch == -1) ch = EOF;
		}

	}

	void AddCh() {
		if (tlen >= tval.Length) {
			char[] newBuf = new char[2 * tval.Length];
			Array.Copy(tval, 0, newBuf, 0, tval.Length);
			tval = newBuf;
		}
		if (ch != EOF) {
			tval[tlen++] = (char) ch;
			NextCh();
		}
	}




	void CheckLiteral() {
		switch (t.val) {
			case "entity": t.kind = 8; break;
			case "inherits": t.kind = 9; break;
			case "query": t.kind = 10; break;
			case "order": t.kind = 11; break;
			case "by": t.kind = 12; break;
			case "returns": t.kind = 13; break;
			case "fetch": t.kind = 14; break;
			case "update": t.kind = 15; break;
			case "fields": t.kind = 16; break;
			case "code": t.kind = 17; break;
			case "import": t.kind = 18; break;
			case "namespace": t.kind = 19; break;
			case "readonly": t.kind = 20; break;
			case "protected": t.kind = 21; break;
			case "private": t.kind = 22; break;
			case "like": t.kind = 23; break;
			case "compute": t.kind = 24; break;
			case "default": t.kind = 25; break;
			case "type": t.kind = 26; break;
			case "connection": t.kind = 27; break;
			default: break;
		}
	}

	Token NextToken() {
		while (ch == ' ' ||
			ch >= 9 && ch <= 10 || ch == 13
		) NextCh();

		t = new Token();
		t.pos = pos; t.col = col; t.line = line; 
		int state;
		if (start.ContainsKey(ch)) { state = (int) start[ch]; }
		else { state = 0; }
		tlen = 0; AddCh();
		
		switch (state) {
			case -1: { t.kind = eofSym; break; } // NextCh already done
			case 0: { t.kind = noSym; break; }   // NextCh already done
			case 1:
				if (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 1;}
				else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
			case 2:
				if (ch >= '0' && ch <= '9') {AddCh(); goto case 2;}
				else {t.kind = 2; break;}
			case 3:
				if (ch <= 92 || ch >= '^' && ch <= 65535) {AddCh(); goto case 3;}
				else if (ch == ']') {AddCh(); goto case 4;}
				else {t.kind = noSym; break;}
			case 4:
				{t.kind = 3; break;}
			case 5:
				if (ch <= 'Z' || ch == 92 || ch >= '^' && ch <= 65535) {AddCh(); goto case 5;}
				else if (ch == ']') {AddCh(); goto case 16;}
				else if (ch == '[') {AddCh(); goto case 6;}
				else {t.kind = noSym; break;}
			case 6:
				if (ch <= 'Z' || ch >= 92 && ch <= 65535) {AddCh(); goto case 5;}
				else {t.kind = noSym; break;}
			case 7:
				{t.kind = 4; break;}
			case 8:
				if (ch <= '!' || ch >= '#' && ch <= 65535) {AddCh(); goto case 8;}
				else if (ch == '"') {AddCh(); goto case 9;}
				else {t.kind = noSym; break;}
			case 9:
				{t.kind = 5; break;}
			case 10:
				if (ch == 10) {AddCh(); goto case 11;}
				else if (ch <= 9 || ch >= 11 && ch <= 65535) {AddCh(); goto case 10;}
				else {t.kind = noSym; break;}
			case 11:
				{t.kind = 6; break;}
			case 12:
				if (ch <= ')' || ch >= '+' && ch <= 65535) {AddCh(); goto case 12;}
				else if (ch == '*') {AddCh(); goto case 17;}
				else {t.kind = noSym; break;}
			case 13:
				{t.kind = 7; break;}
			case 14:
				if (ch <= 'Z' || ch >= 92 && ch <= 65535) {AddCh(); goto case 3;}
				else if (ch == '[') {AddCh(); goto case 5;}
				else {t.kind = noSym; break;}
			case 15:
				if (ch == '/') {AddCh(); goto case 10;}
				else if (ch == '*') {AddCh(); goto case 12;}
				else {t.kind = noSym; break;}
			case 16:
				if (ch <= 92 || ch >= '^' && ch <= 65535) {AddCh(); goto case 5;}
				else if (ch == ']') {AddCh(); goto case 7;}
				else {t.kind = noSym; break;}
			case 17:
				if (ch <= '.' || ch >= '0' && ch <= 65535) {AddCh(); goto case 12;}
				else if (ch == '/') {AddCh(); goto case 13;}
				else {t.kind = noSym; break;}
			case 18:
				{t.kind = 28; break;}
			case 19:
				{t.kind = 29; break;}
			case 20:
				{t.kind = 30; break;}
			case 21:
				{t.kind = 31; break;}
			case 22:
				{t.kind = 32; break;}
			case 23:
				{t.kind = 33; break;}
			case 24:
				{t.kind = 34; break;}
			case 25:
				{t.kind = 35; break;}
			case 26:
				{t.kind = 36; break;}
			case 27:
				{t.kind = 37; break;}
			case 28:
				{t.kind = 38; break;}

		}
		t.val = new String(tval, 0, tlen);
		return t;
	}
	
	// get the next token (possibly a token already seen during peeking)
	public Token Scan () {
		if (tokens.next == null) {
			return NextToken();
		} else {
			pt = tokens = tokens.next;
			return tokens;
		}
	}

	// peek for the next token, ignore pragmas
	public Token Peek () {
		do {
			if (pt.next == null) {
				pt.next = NextToken();
			}
			pt = pt.next;
		} while (pt.kind > maxT); // skip pragmas
	
		return pt;
	}

	// make sure that peeking starts at the current scan position
	public void ResetPeek () { pt = tokens; }

} // end Scanner
}
